Skip to content

[Autorun] New plugin#1582

Open
WrapEarnPass wants to merge 27 commits into
geany:masterfrom
WrapEarnPass:autorun
Open

[Autorun] New plugin#1582
WrapEarnPass wants to merge 27 commits into
geany:masterfrom
WrapEarnPass:autorun

Conversation

@WrapEarnPass

@WrapEarnPass WrapEarnPass commented Jun 23, 2026

Copy link
Copy Markdown

What this plugin does:
intercept the Geany signals in an extensible way (more handlers can be added later with minimal refactoring).

The handlers that are implemented in this release:
document-before-save
document-save

When the plugin loads it looks for filetypes.EXT like build does. If it finds any [autorun] sections, it loads the matching keys as commands to be executed during one of the handlers.
If there is a Project open, it looks for [autorun] in the Project .geany, and overrides any filetypes.EXT handlers.

When a instrumented handler fires, it processes the list of commands that have been loaded for that signal.

My specific use case is I have no code formatting conformity when I am writing. I rely heavily on clang-format. I need it to run on-save, on every save or my code looks like crep.
I also like for tidy to remind me if my code has any known issues as I write it, so I don't have to go back from build to fix a bunch of stuff at once.

I configured a filetypes.c to attach clang-format to document-before-save, and clang-tidy to document-save.

I also use cmake, but cmake-format doesnt do stdin, so I implemented a dump of scintilla to tmpfile so I could run cmake-format on document-before-save.

If the commands at document-before-save succeeds, scintilla is overwritten by either the contents of the tmpfile (if changed) or the results of stdout. stderr goes to Compiler.

If the commands at document-save succeeds, that is done at the file level, so I just report the stdout and stderr to Compiler.

Overall success/failure/could not run command is attached as a Status.

I try to be nice about attaching 'jump to scintilla' for messages, but am ultimately dependent on Compiler for that.

The UI for Auto-run, as submitted, only allows setting 1 before-save and 1 on-save command per filetype in the Project properties. Editing the filetypes.EXT or .geany manually, you can attach up to 100 total commands.
I use this to allow me to run on-save for both clang-tidy as I prefer, and cppcheck as Geany prefers on code I am submitting to Geany.

I used a very similar command format to Geany>Project>Build which supports inline replacements
Examples:
filetypes.c

[autorun]
OS_00_CM=clang-tidy %f
OS_00_WD=%d

projectfile.geany

[autorun]
C_BS_01_CM=cppcheck %f
C_BS_01_WD=%d

I couldn't come up with a use case for before-save/on-save for None files, so this doesn't support actions on None files.

add globals def
Update README to account for non-stdin tools
add _free for autorun defined types
wire up _free for autorun defined types
add spawn controller impl
Remove my mktemp impl as gtk has one
Flesh out the spawn.c handler
Ensure %f %d and %p are honored in spawn
Prevent spawn from actually editing before-save until testing completed
document-save is handled entirely externally and is live
***THIS WILL ALTER YOUR FILESYSTEM IF YOU ADD A HANDLER***
Updates geany#1577
Update all documents with .clang-format backed formatting changes
Clarified README to point to Status and Compiler instead of Terminal
reordered all includes to make them uniform
ensure translation setup was done
fixed tempfile writes for %a tools
added CMakeLists.txt as a template for clang-tidy runs

@WrapEarnPass WrapEarnPass left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is missing a menu system, but that was a nice to have anyway.
It is still a <1.0 release because I want at least a few other people to test it for use cases I can't reproduce before it is 1.0.
I am using it daily now, so there is a chance I will find errors for the use case of "on linux"
I could probably make the stdout/stderr handler smarter to allow for formatting arbitrary messages, but that wasn't really in scope for the request.

@WrapEarnPass

Copy link
Copy Markdown
Author

So, if this doesn't get a review or pull soon (like in 24 hours), I will have menu working.
No rush for anyone or anything, just setting expectations.

Tools>Auto-run already wired up to reload configs.
image

Project>Auto-run is currently just mocked, but I should be able to wire the Keyfile handler by tomorrow
image

Fixed a parsing issue with empty string commands
Had to turn project-save back on in order to store Project gkeyfile changes
@WrapEarnPass

Copy link
Copy Markdown
Author

There wasn't any interim actions here, so I rolled the Menu changes into this Pull as well.
Now other than stability/user acceptance testing, this is a 1.0 feature complete release.

@b4n

b4n commented Jun 24, 2026

Copy link
Copy Markdown
Member

Maybe you could explain what this new plugin does in the OP, so we know what we're looking at?

@WrapEarnPass

WrapEarnPass commented Jun 24, 2026

Copy link
Copy Markdown
Author

@b4n

Maybe you could explain what this new plugin does in the OP, so we know what we're looking at?

I am not sure what OP means in this context.
I opened #1577 to ensure that my proposal didn't crud up someone else's bug report/feature request if I was entirely off base about it. Do you want me to add a comment to #733, even though #733 is technically against Workbench? Or do you mean lay out 'about this plugin' in this PR initial comment?

@WrapEarnPass

Copy link
Copy Markdown
Author

Moved this from the initial comment so I could add "about this plugin" to the initial comment.

$ git log | head
commit ae7e926e126a46a09eb3824886ce89a882a270ef
$ make clean && make install
rm -f  autorun.la
rm -rf .libs _libs
rm -f *.o
rm -f *.lo
rm -f ./so_locations
  CC       autorun_la-autorun.lo
  CC       autorun_la-plugin_main.lo
  CC       autorun_la-spawn.lo
  CC       autorun_la-utils.lo
  CCLD     autorun.la
$ cppcheck *.c --enable=all --check-level=exhaustive 2>&1 | grep -vP '(<glib/gstdio.h>|<geanyplugin.h>|<gtk/gtk.h>|<spawn.h>)'
Checking autorun.c ...
1/4 files checked 23% done
Checking plugin_main.c ...
plugin_main.c:117:42: style: Parameter 'plugin' can be declared as pointer to const. However it seems that 'autorun_cleanup' is a callback function, if 'plugin' is declared with const you might also need to cast function pointer(s). [constParameterCallback]
static void autorun_cleanup(GeanyPlugin* plugin, gpointer pdata) {
                                         ^
plugin_main.c:136:27: note: You might need to cast the function pointer here
 plugin->funcs->cleanup = autorun_cleanup;
                          ^
plugin_main.c:117:42: note: Parameter 'plugin' can be declared as pointer to const
static void autorun_cleanup(GeanyPlugin* plugin, gpointer pdata) {
                                         ^
2/4 files checked 43% done
Checking spawn.c ...
spawn.c:136:46: style: Condition 'success' is always true [knownConditionTrueFalse]
    if (g_strcmp0(interceptor, "BS") == 0 && success && !tmpfile) {
                                             ^
spawn.c:49:17: style: Variable 'cmd' can be declared as pointer to const [constVariablePointer]
   AUTORUN_CMD* cmd = ((AUTORUN_CMD*)elem->data);
                ^
spawn.c:179:15: style: Variable 'lineptr' can be declared as pointer to const [constVariablePointer]
       gchar* lineptr = g_strstr_len(stdout_data->str, 255, ":");
              ^
spawn.c:203:15: style: Variable 'lineptr' can be declared as pointer to const [constVariablePointer]
       gchar* lineptr = g_strstr_len(stdout_data->str, 255, ":");
              ^
spawn.c:221:15: style: Variable 'lineptr' can be declared as pointer to const [constVariablePointer]
       gchar* lineptr = g_strstr_len(stdout_data->str, 255, ":");
              ^
spawn.c:238:15: style: Variable 'lineptr' can be declared as pointer to const [constVariablePointer]
       gchar* lineptr = g_strstr_len(stdout_data->str, 255, ":");
              ^
3/4 files checked 86% done
Checking utils.c ...
4/4 files checked 100% done
plugin_main.c:124:0: style: The function 'geany_load_module' is never used. [unusedFunction]
void geany_load_module(GeanyPlugin* plugin) {
autorun.c:26:0: style: The function 'autorun_cmd_new' should have static linkage since it is not used outside of its translation unit. [staticFunction]
AUTORUN_CMD* autorun_cmd_new() {
^
autorun.c:38:0: style: The function 'autorun_cmd_free' should have static linkage since it is not used outside of its translation unit. [staticFunction]
void autorun_cmd_free(AUTORUN_CMD* cmd) {
^
nofile:0:0: information: Active checkers: 109/856 (use --checkers-report=<filename> to see details) [checkersReport]

I know this project doesnt use clang-tidy, but I happen to use it, so I set it up too.

$ clang-tidy-19 -p autorun/build *.c 
[1/4] Processing file autorun/src/autorun.c.
[2/4] Processing file autorun/src/plugin_main.c.
1 error generated.
Error while processing autorun/src/plugin_main.c.
[3/4] Processing file autorun/src/spawn.c.
1 error generated.
Error while processing autorun/src/spawn.c.
[4/4] Processing file autorun/src/utils.c.
1 error generated.
Error while processing autorun/src/utils.c.
autorun/src/plugin_main.c:132:19: error: use of undeclared identifier 'LOCALEDIR' [clang-diagnostic-error]
  132 |         main_locale_init(LOCALEDIR, GETTEXT_PACKAGE);
      |                          ^
Found compiler error(s).

That looks like clang-tidy doesn't get the po/ loader magic, but nothing else reported.

built against geany/geany@98bcbee
geany --verbose has no messages

Closes #1577
May close #733

@WrapEarnPass

WrapEarnPass commented Jun 26, 2026

Copy link
Copy Markdown
Author

There's not been any changes here, and I made the output more intelligent. Now it checks to see if any output contains an open filename and colors it red so Compiler can use it's internal logic to build clicky-mapping. Pushing those changes into this Pull.

try to use Compilers built in clicky-jump instead of creating a new parser
Update README to note the change in the output location
@WrapEarnPass

WrapEarnPass commented Jun 26, 2026

Copy link
Copy Markdown
Author

Again nothing has been frozen here, so I am pushing my changes for async into this pull.
It's really much nicer for slow commands like cppcheck.
Peek 2026-06-26 13-14

Fix uniform AUTORUN_ namespaces
Updated README to reflect async changes
Fix progressbar handler actually works now
@WrapEarnPass

Copy link
Copy Markdown
Author

I got clangd setup on my geany 2.1 system install, and recompiled this for 2.1.
clang-tidy found a use after free.

/home/build/Projects/geany-plugins/geany-plugins/autorun/src/spawn.c:189:54: warning: Use of memory after it is freed [clang-analyzer-unix.Malloc]
189 | ui_set_statusbar(FALSE, _("Auto-run finished %s"), exit_data->doc->file_name);

Pushing that fix.

In other news, this code works in geany/2.1, which leads to a question. Is 2.1 closed? Can I submit this to 2.1 as well?

housekeeping comments to ensure they are uniformly formatted
change macro gluing tooltips to strings to fix code formatting
@WrapEarnPass

Copy link
Copy Markdown
Author

With a sufficiently malicious filename, spawn cannot be trusted. Directory comes from Geany runtime environment, and other arguments from user preferences, but the filename could be an attack vector in an "automatic run" environment.
06103a4 escapes the filename to ensure it is not naively passed to spawn.

As noted in https://docs.gtk.org/glib/func.shell_quote.html this is a best effort.

g_shell_quote is not portable

This reverts commit 06103a4.
update README with notes about filenames
this commit is expected long term approach for geany#1584 for Auto-run
fix used a uint instead of a guint
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants